﻿#include <cargs.h>
#include <string.h>
#include <ctype.h>
#include <conio.h>
#include <stdio.h>

#include "opts.h"

#define DEFAULT_SRC_PORT 1234

static struct cag_option options[] = {
	{.identifier = 'h',
	 .access_letters = "h",
	 .access_name = "help",
	 .description = "Show this help and exit"},
	{.identifier = 'm',
	 .access_letters = "m",
	 .access_name = "macaddr",
	 .value_name = "VALUE",
	 .description = "Custom source MAC address"},
	{.identifier = 'i',
	 .access_letters = "i",
	 .access_name = "ip",
	 .value_name = "VALUE",
	 .description = "Custom source IP address"},
	{.identifier = 'g',
	 .access_letters = "g",
	 .access_name = "gateway",
	 .value_name = "VALUE",
	 .description = "Custom gateway MAC address"},
	{.identifier = 'p',
	 .access_letters = "p",
	 .access_name = "port",
	 .value_name = "VALUE",
	 .description = "Custom UDP source port"},
};

Config parse_config(int argc, char **argv)
{
	None_or_Addresses default_addrs = get_default_addrs();

	Addresses opt;

	if (default_addrs.tag == None_Addresses)
	{
		// 获取默认的地址信息失败，需要用户手动输入全部地址信息。
		// 因为还没想好怎么处理，所以如果遇到就直接退出
		fprintf(stderr, "Error: Cannot get default addresses\n");
		exit(1);
	}
	else if (default_addrs.tag == Some_Addresses)
	{
		opt = default_addrs.some;
	}

	uint16_t s_port = DEFAULT_SRC_PORT;

	cag_option_context context;
	cag_option_prepare(&context, options, CAG_ARRAY_SIZE(options), argc, argv);
	int ret;
	while (cag_option_fetch(&context))
	{
		switch (cag_option_get_identifier(&context))
		{
		case 'h':
			print_help();
			exit(0);
		case 'm':
			ret = sscanf_s(cag_option_get_value(&context), "%hhX-%hhX-%hhX-%hhX-%hhX-%hhX",
						   &opt.source_mac_addr[0], &opt.source_mac_addr[1], &opt.source_mac_addr[2],
						   &opt.source_mac_addr[3], &opt.source_mac_addr[4], &opt.source_mac_addr[5]);

			if (ret != 6)
			{
				fprintf(stderr, "Error: Invalid source MAC address for option 'm'\n");
				exit(1);
			}
			break;
		case 'i':
			ret = sscanf_s(cag_option_get_value(&context), "%hhu.%hhu.%hhu.%hhu",
						   &opt.source_ip_addr[0], &opt.source_ip_addr[1], &opt.source_ip_addr[2],
						   &opt.source_ip_addr[3]);
			if (ret != 4)
			{
				fprintf(stderr, "Error: Invalid source IPv4 address for option 'i'\n");
				exit(1);
			}
			break;
		case 'g':
			ret = sscanf_s(cag_option_get_value(&context), "%hhX-%hhX-%hhX-%hhX-%hhX-%hhX",
						   &opt.des_mac_addr[0], &opt.des_mac_addr[1], &opt.des_mac_addr[2],
						   &opt.des_mac_addr[3], &opt.des_mac_addr[4], &opt.des_mac_addr[5]);
			if (ret != 6)
			{
				fprintf(stderr, "Error: Invalid gateway MAC address for option 'g'\n");
				exit(1);
			}
			break;
		case 'p':
			ret = sscanf_s(cag_option_get_value(&context), "%hu", &s_port);
			if (ret != 1)
			{
				fprintf(stderr, "Error: Invalid port number for option 'p'\n");
				exit(1);
			}
			break;
		case '?':
			cag_option_print_error(&context, stderr);
			break;
		}
	}

	Config config;

	config.s_port = s_port;
	memcpy(config.src_mac, opt.source_mac_addr, sizeof(config.src_mac));
	memcpy(config.src_ip, opt.source_ip_addr, sizeof(config.src_ip));
	memcpy(config.gtw_mac, opt.des_mac_addr, sizeof(config.gtw_mac));

	/////////////////////////////////////////////////////////
	//
	// 下面开始读取命令行输入的非选项参数。
	// 步骤如下：
	// 1. 读第一个参数
	//     如果没有参数，使用默认ip地址 -> 进入交互模式
	//     如果是ip地址 -> 进入交互模式
	//     如果不是，则认为是域名，继续读取参数
	// 2. 读第二个参数
	//     如果没有第二个参数，使用默认ip地址 -> 进入非交互模式
	//     如果是ip地址，继续读取参数
	//     如果不是，则输出“无法找到的服务器地址”，使用默认地址，继续读取参数
	// 3. 读第三个参数
	//     如果没有第三个参数，进入非交互模式 -> 结束程序
	//     如果有第三个参数，输出帮助信息 -> 结束程序
	//
	/////////////////////////////////////////////////////////

	// 此处获得了指向命令行输入的第一个非选项参数的索引。
	int arg_index = cag_option_get_index(&context);

	if (arg_index >= argc)
	{
		/* code */
		printf("Missing DNS server address input!\n");
		printf("Use default DNS server!\n");
		memcpy(config.dns_server, opt.des_ip_addr, sizeof(config.dns_server));
		config.domain_name = NULL;

		return config;
	}

	char *first_arg = context.argv[arg_index];

	if (is_ipv4_address(first_arg))
	{
		ret = sscanf_s(first_arg, "%hhu.%hhu.%hhu.%hhu",
					   &config.dns_server[0], &config.dns_server[1], &config.dns_server[2],
					   &config.dns_server[3]);
		if (ret != 4)
		{
			fprintf(stderr, "Error: Invalid dns IPv4 address\n");
			exit(1);
		}
		config.domain_name = NULL;

		return config;
	}

	config.domain_name = first_arg;

	arg_index++;
	if (arg_index >= argc)
	{
		memcpy(config.dns_server, opt.des_ip_addr, sizeof(config.dns_server));
		return config;
	}

	char *secend_arg = context.argv[arg_index];

	if (is_ipv4_address(secend_arg))
	{
		ret = sscanf_s(secend_arg, "%hhu.%hhu.%hhu.%hhu",
					   &config.dns_server[0], &config.dns_server[1], &config.dns_server[2],
					   &config.dns_server[3]);
		if (ret != 4)
		{
			fprintf(stderr, "Error: Invalid dns IPv4 address\n");
			exit(1);
		}
	}
	else
	{
		printf("Invalid DNS server address input!\n");
		printf("Use default DNS server!\n");
		memcpy(config.dns_server, opt.des_ip_addr, sizeof(config.dns_server));
	}

	arg_index++;

	if (arg_index >= argc)
	{
		return config;
	}
	else
	{
		printf("Too many arguments!\n");
		print_help();
		exit(1);
	}

	// return config;
}

void print_help(void)
{
	printf("my_nslookup - A simple nslookup tool\n");
	printf("\nUsages:\n");
	printf("  my_nslookup [<domain>] [<DNS>] [-<option>=<VALUE>]...\n");
	printf("\nOptions:\n");
	cag_option_print(options, CAG_ARRAY_SIZE(options), stdout);
	printf("\nNote: It is not recommended to enter a customized local mac address, gateway mac address, and local IP address,\n");
	printf("      but should be obtained automatically by this program.\n");
	printf("      Because if you enter it incorrectly, you may not receive the reply packet.\n");
	printf("\nCommands:\n");
	printf("  help\t\tShow this help tips\n");
	printf("  exit\t\tExit the program\n");
}

int is_ipv4_address(const char *str)
{
	int dots = 0;
	for (int i = 0; str[i]; i++)
	{
		if (str[i] == '.')
		{
			dots++;
			if (dots > 3)
			{
				return 0;
			}
		}
		else if (!isdigit((unsigned char)str[i]))
		{
			return 0;
		}
	}
	return dots == 3;
}

void exit_with_message(const char* format, ...) {
	va_list args;
	va_start(args, format);
	vfprintf(stderr, format, args);
	va_end(args);
	fprintf(stderr, "\n");
	printf("按任意键退出...\n");
	_getch(); // 等待用户按键。
	exit(1);
}